from types import FunctionType
from functools import wraps
from django.http import HttpResponse, QueryDict
from django.shortcuts import render, redirect
from django.urls import path, re_path, reverse
from django.utils.safestring import mark_safe

from .pagination import Pagination
from .baseForm import StarkModelForm


class StarkHandler(object):
    limit = 10
    has_add_btn = True
    model_from_class = None  # 自定义ModelForm

    def get_pk(self, obj):
        """
        获取主键
        :param obj:
        :return:
        """
        return getattr(obj, 'pk')

    def display_edit(self, obj=None, is_header=False):

        if is_header:
            return '编辑'
        url = self.reverse_url_name(self.get_edit_url_name, args=(self.get_pk(obj),))
        return mark_safe(
            f'<a type="button" class="layui-btn layui-btn-sm layui-btn-warm" href={url}><i class="layui-icon">&#xe642;</i></a>')

    def display_del(self, obj=None, is_header=False):

        if is_header:
            return '删除'
        url = self.reverse_url_name(self.get_del_url_name, args=(self.get_pk(obj),))
        return mark_safe(
            f'<a type="button" class="layui-btn layui-btn-sm layui-btn-danger" href={url}><i class="layui-icon">&#xe640;</i></a>')

    list_display = []  # 页面显示字段

    def __init__(self, site, model_class, prefix, pk_regx):
        """

        :param site: StarkSite
        :param model_class: 模型类
        :param prefix: 路由前缀
        :param pk_regx: pk正则
        """
        self.site = site
        self.model_class = model_class
        self.app_label = self.model_class._meta.app_label
        self.model_name = self.model_class._meta.model_name
        self.prefix = prefix
        self.pk_regx = pk_regx

    def get_add_btn(self):
        """
        添加按钮
        :return:
        """
        if self.has_add_btn:
            url = self.reverse_url_name(self.get_add_url_name)
            return f'<a href="{url}" class="layui-btn layui-btn-warm">添加</a>'
        return None

    def reverse_url_name(self, url_name, args=None):
        """
        根据url_name 反向生成
        :param url_name:
        :param args:
        :return:
        """
        url = reverse(f'{self.site.namespace}:{url_name}', args=args)
        if self.request.GET:
            params = self.request.GET.urlencode()
            query = QueryDict(mutable=True)
            query['_filter'] = params
            url = f'{url}?{query.urlencode()}'
        return url

    def get_list_display(self):
        """
        获取list_display：
            可根据用户类型重写，自定义显示
        :return:
        """
        return self.list_display

    def get_field_verbose_name(self, field):
        """
        获取该字段的verbose_name
        :param field:
        :return:
        """
        return self.model_class._meta.get_field(field).verbose_name

    def get_header_or_row(self, row=None, is_header=False):
        """
        获取表头或者每一行要显示的数据
        :param row: 
        :param is_header: 
        :return: 
        """
        row_li = []
        list_display = self.get_list_display()
        if list_display:
            for field_or_func in list_display:
                if isinstance(field_or_func, FunctionType):
                    show_data = field_or_func(self, is_header=is_header) if is_header else field_or_func(self, row)
                else:
                    show_data = self.get_field_verbose_name(field_or_func) if is_header else getattr(row, field_or_func)
                row_li.append(show_data)
        else:
            row_li.append(self.model_name if is_header else row)
        return row_li

    def body_list_queryset(self, queryset):
        """
        获取body数据
        :param queryset:
        :return:
        """
        return [self.get_header_or_row(item) for item in queryset]

    def filter_queryset(self, query):
        """
        过滤数据
        :param query:
        :return:
        """

        queryset = self.model_class.objects.all()
        return queryset

    def pagination(self, request, queryset):
        """
        分页
        :param request:
        :param queryset:
        :return:
        """
        page = int(request.GET.get('page', 1))
        pager = Pagination(
            page,
            queryset,
            self.limit,
        )
        return pager

    def layout_context(self):
        # 渲染菜单
        return {
            "all_menu": self.site.all_menu
        }

    def list_view(self, request):
        """
        列表页面
        :param request:
        :return:

        """
        # ############ 表头处理
        # 有list_display, 按list_display显示
        # 没有就显示表名称
        header_list = self.get_header_or_row(is_header=True)

        # ############# 表内容
        # [[xx,xxx],[xxx,xxx],[xxx,xxx]]
        # 有list_display, 按list_display显示
        # 没有就显示对象

        # 过滤
        queryset = self.filter_queryset(request.GET)
        pager = self.pagination(request, queryset)
        body_list = self.body_list_queryset(pager.queryset)

        add_btn = self.get_add_btn()

        context = self.layout_context()
        context.update({
            'body_list': body_list,
            'header_list': header_list,
            'pager': pager,
            'base_url': request.path_info,
            'add_btn': add_btn,
        })
        return render(request, 'stark/list.html', context=context)

    def get_model_form_class(self):
        """
        获取动态表单
        :return:
        """
        if self.model_from_class:
            return self.model_from_class

        class DynamicModelForm(StarkModelForm):
            class Meta:
                model = self.model_class
                fields = '__all__'

        return DynamicModelForm

    def reverse_list_url(self):
        # 返回list页面
        url = reverse(f'{self.site.namespace}:{self.get_list_url_name}')
        params = self.request.GET.get('_filter')
        if not params:
            return url
        return f'{url}?{params}'

    def save(self, form):
        """
        扩展form.save  自定义字段保存
        :param form:
        :return:
        """
        # form.instance.depart_id = 1
        form.save()

    def add_view(self, request):
        """
        添加页面
        :param request:
        :return:
        """
        model_form = self.get_model_form_class()
        if request.method == 'POST':
            form = model_form(data=request.POST)
            if form.is_valid():
                self.save(form)
                url = self.reverse_list_url()
                return redirect(url)

        form = model_form()

        context = self.layout_context()
        context.update({
            'form': form,
        })
        return render(request, 'stark/edit.html', context=context)

    def edit_view(self, request, pk):
        """
        编辑页面
        :param request:
        :return:0
        """
        curr_obj = self.model_class.objects.filter(pk=pk).first()
        url = self.reverse_list_url()
        context = self.layout_context()

        if not curr_obj:
            context.update({
                'cancel': url,
            })
            return render(request, 'stark/404.html', context=context)
        model_form = self.get_model_form_class()
        if request.method == 'GET':
            form = model_form(instance=curr_obj)
            context.update({
                'form': form,
            })
            return render(request, 'stark/edit.html', context=context)
        form = model_form(data=request.POST, instance=curr_obj)
        if form.is_valid():
            self.save(form)
            return redirect(url)
        context.update({
            'form': form,
        })
        return render(request, 'stark/edit.html', context=context)

    def del_view(self, request, pk):
        """
        删除页面
        :param request:
        :return:
        """
        curr_obj = self.model_class.objects.filter(pk=pk)
        url = self.reverse_list_url()
        context = self.layout_context()
        context.update({
            'cancel': url,
        })
        if not curr_obj.exists():
            return render(request, 'stark/404.html', context=context)
        if request.method == 'GET':
            return render(request, 'stark/delete.html', context=context)
        curr_obj.delete()
        return redirect(url)

    def add_self_request(self, func):
        @wraps(func)  # 保留原信息
        def inner(request, *args, **kwargs):
            self.request = request
            return func(request, *args, **kwargs)

        return inner

    @property
    def base_url(self):
        """
        基础url
        :return:
        """
        if self.prefix:
            return f'{self.app_label}/{self.model_name}/{self.prefix}'
        return f'{self.app_label}/{self.model_name}'

    @property
    def get_pk_regx(self):
        """
        pk正则
        :return:
        """
        if self.pk_regx:
            return f'(?P<pk>{self.pk_regx})'
        return '(?P<pk>\d+)'

    @property
    def get_url_name_prefix(self):
        """
        路由名称
        :return:
        """
        if self.prefix:
            return f'{self.app_label}_{self.model_name}_{self.prefix}'
        return f'{self.app_label}_{self.model_name}'

    @property
    def get_list_url_name(self):
        """
        获取 列表页面url_name
        :return:
        """
        return f'{self.get_url_name_prefix}_list'

    @property
    def get_add_url_name(self):
        """
        获取 添加页面url_name
        :return:
        """
        return f'{self.get_url_name_prefix}_add'

    @property
    def get_edit_url_name(self):
        """
        获取 编辑页面url_name
        :return:
        """
        return f'{self.get_url_name_prefix}_edit'

    @property
    def get_del_url_name(self):
        """
        获取 删除页面url_name
        :return:
        """
        return f'{self.get_url_name_prefix}_del'

    def get_urls(self):
        """
        生成基础 4个urls
            : 如需要减少基本url ，重写即可
        :return:
            1. [path('/xx/xx/list/', views.list, name='xx_xx_list')...]
            2. [path('/xx/xx/prefix/list/', views.list, name='xx_xx_prefix_list')...]
        """
        patterns = [
            path(f'{self.base_url}/list/', self.add_self_request(self.list_view), name=self.get_list_url_name),
            path(f'{self.base_url}/add/', self.add_self_request(self.add_view), name=self.get_add_url_name),
            re_path(f'{self.base_url}/edit/{self.get_pk_regx}/', self.add_self_request(self.edit_view),
                    name=self.get_edit_url_name),
            re_path(f'{self.base_url}/del/{self.get_pk_regx}/', self.add_self_request(self.del_view),
                    name=self.get_del_url_name),
        ]
        return patterns

    def extra_urls(self):
        """
        额外的urls
            :return:
            [path(f'{self.base_url}/xxx/', self.xxx_view, name=f'{self.model_name}_xxx')]
        """
        patterns = []
        return patterns
